import ms from 'ms';
import fetch from 'fermionjs/lib/fetch';
import pkg from '../../../../../package.json';
import {
	handleTaskCompletedBuilder,
	handleTaskFailed as handleTaskFailedBuilder,
} from '../../index';
import authHeaderBuilder from './authHeaderBuilder';
import { taskIdsKey } from './index';
import warningCheck from './warningCheck';
import responseValidate from './responseValidate';
import fillMissingNode from './fillMissingNode';
import { NODE_ID_LABEL_MAP2 } from '../../../../common/constants';
const { name } = pkg;

export default function aviliableTask(server) {
	const {
		app: {
			redis,
			config: {
				[name]: {
					aviliableRepeatTask: { getResultApi, username, apiId, apiKey },
				},
			},
		},
	} = server;
	const handleTaskCompleted = handleTaskCompletedBuilder(server);
	const handleTaskFailed = handleTaskFailedBuilder(server);

	return async job => {
		server.log.info('[可用性任务状态监测]定时任务开始执行');

		let taskTidDataMap;
		try {
			taskTidDataMap = await redis.hgetall(taskIdsKey);
		} catch (error) {
			return server.log.error(
				{ error },
				'[可用性任务状态监测]获取Redis任务ID失败'
			);
		}
		if (Object.keys(taskTidDataMap).length === 0) {
			return server.log.info('[可用性任务状态监测]没有正在执行的监测任务');
		}

		const taskTids = Object.keys(taskTidDataMap);

		let taskStatusResp;
		let api;
		try {
			const params = [
				['username', username],
				['time', (Date.now() / 1000) | 0],
			];
			const token = authHeaderBuilder(apiId, apiKey, params);
			api = `${getResultApi}?${new URLSearchParams(params).toString()}`;
			taskStatusResp = await fetch(api, {
				method: 'POST',
				headers: {
					AUTHORIZATION: token,
					'Content-Type': 'application/json',
				},
				body: JSON.stringify({ tids: taskTids }),
			});
			server.log.debug(
				{
					job: job.data,
					response: taskStatusResp,
				},
				'[可用性任务状态监测]扫描检查任务状态'
			);
			if (taskStatusResp.code !== 0) {
				return server.log.info(
					{ api, response: taskStatusResp },
					'[可用性任务状态监测]扫描检查任务状态出错'
				);
			}
		} catch (error) {
			return server.log.error(
				{ api, error },
				'[可用性任务状态监测]获取任务状态失败'
			);
		}

		const tids = [];
		for (const response of taskStatusResp.data || []) {
			const tid = response.tid;
			if (!tid) continue;

			tids.push(tid);
			const taskData = JSON.parse(taskTidDataMap[tid] || '{}');
			const {
				target,
				collect: { area },
				alert: { respTime, nodeCount, statusCodeRange },
			} = taskData;
			server.log.debug(
				{
					tid,
					target,
					response: JSON.stringify(response, null, 4),
				},
				'[可用性任务状态监测]任务执行成功'
			);

			let resp = responseValidate(response);
			resp = fillMissingNode(area, resp, NODE_ID_LABEL_MAP2);

			const { warnings, addition } = warningCheck({
				target,
				response: resp,
				rules: {
					respTime,
					nodeCount,
					statusCodeRange,
				},
			});
			const abnormalNodes = warnings.reduce((set, w) => {
				w.detail.area.forEach(node => set.add(node));
				return set;
			}, new Set());
			const nodeTotalCount = addition.detail.length || 0;
			const nodeAbnormalCount = abnormalNodes.size;

			const ret = {
				warnings,
				addition: {
					...addition,
					nodeTotalCount,
					nodeAbnormalCount,
				},
			};

			try {
				await handleTaskCompleted({ data: taskData }, ret);
			} catch (error) {
				server.log.error(
					{ error, taskData, ret },
					'[可用性任务状态监测]处理任务完成逻辑失败'
				);
				handleTaskFailed({ data: taskData, id: taskData.id }, error);
				continue;
			}
		}

		if (tids.length) {
			try {
				await redis.hdel(taskIdsKey, tids);
			} catch (error) {
				return server.log.error(
					{ error, tids },
					'[可用性任务状态监测]删除Redis任务ID失败'
				);
			}
		}

		tids.forEach(id => {
			delete taskTidDataMap[id];
		});
		await Promise.all(
			Object.entries(taskTidDataMap).map(async ([id, data]) => {
				if (Date.now() - (data.createTime || 0) > ms('5m')) {
					// 任务超过5分钟没有结果，标记为失败
					try {
						await handleTaskFailed(
							{ data, id: data.id },
							new Error('[可用性任务状态监测]任务超过5分钟没有结果')
						);
						await redis.hdel(taskIdsKey, id);
					} catch (error) {
						return server.log.error(
							{ error, tids },
							'[可用性任务状态监测]删除Redis任务ID失败'
						);
					}
				}
			})
		);

		server.log.info('[可用性任务状态监测]定时任务执行完成');
	};
}
